let db;
let idb_helper;

export default (idb_helper = {
    set_graphene_db: database => {
        db = database;
    },

    trx_readwrite: object_stores => {
        return db.transaction([object_stores], "readwrite");
    },

    on_request_end: request => {
        //return request => {
        return new Promise((resolve, reject) => {
            request.onsuccess = new ChainEvent(
                request.onsuccess,
                resolve,
                request
            ).event;
            request.onerror = new ChainEvent(
                request.onerror,
                reject,
                request
            ).event;
        });
        //}(request)
    },

    on_transaction_end: transaction => {
        return new Promise((resolve, reject) => {
            transaction.oncomplete = new ChainEvent(
                transaction.oncomplete,
                resolve
            ).event;
            transaction.onabort = new ChainEvent(
                transaction.onabort,
                reject
            ).event;
        });
    },

    /** Chain an add event.  Provide the @param store and @param object and
        this method gives you convenient hooks into the database events.

        @param event_callback (within active transaction)
        @return Promise (resolves or rejects outside of the transaction)
    */
    add: (store, object, event_callback) => {
        return (function(object, event_callback) {
            let request = store.add(object);
            let event_promise = null;
            if (event_callback)
                request.onsuccess = new ChainEvent(request.onsuccess, event => {
                    event_promise = event_callback(event);
                }).event;

            let request_promise = idb_helper
                .on_request_end(request)
                .then(event => {
                    //DEBUG console.log('... object',object,'result',event.target.result,'event',event)
                    if (event.target.result != void 0) {
                        //todo does event provide the keyPath name? (instead of id)
                        object.id = event.target.result;
                    }
                    return [object, event];
                });

            if (event_promise)
                return Promise.all([event_promise, request_promise]);
            return request_promise;
        })(object, event_callback); //copy let references for callbacks
    },

    /** callback may return <b>false</b> to indicate that iteration should stop */
    cursor: (store_name, callback, transaction) => {
        return new Promise((resolve, reject) => {
            if (!transaction) {
                transaction = db.transaction([store_name], "readonly");
                transaction.onerror = error => {
                    console.error("ERROR idb_helper.cursor transaction", error);
                    reject(error);
                };
            }

            let store = transaction.objectStore(store_name);
            let request = store.openCursor();
            request.onsuccess = e => {
                let cursor = e.target.result;
                let ret = callback(cursor, e);
                if (ret === false) resolve();
                if (!cursor) resolve(ret);
            };
            request.onerror = e => {
                let error = {
                    error: e.target.error.message,
                    data: e
                };
                console.log("ERROR idb_helper.cursor request", error);
                reject(error);
            };
        }).then();
    },

    autoIncrement_unique: (db, table_name, unique_index) => {
        return db
            .createObjectStore(table_name, {keyPath: "id", autoIncrement: true})
            .createIndex("by_" + unique_index, unique_index, {unique: true});
    }
});

class ChainEvent {
    constructor(existing_on_event, callback, request) {
        // Assigning the constructor arguments to work around hot-reload
        // rewriting breaking the scope of this.event
        this.existing_on_event = existing_on_event;
        this.callback = callback;
        this.event = event => {
            if (event.target.error)
                console.error(
                    "---- transaction error ---->",
                    event.target.error
                );
            //event.request = request
            this.callback(event);
            if (this.existing_on_event) this.existing_on_event(event);
        };
    }
}
